home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-07-09 | 17.5 KB | 636 lines | [TEXT/PJMM] |
- unit TestPicComments;
-
- interface
- uses
- FixMath, PrintTraps, PicComments, Globals;
-
- procedure TextRotationDemo;
- procedure LineLayoutDemo;
- procedure PolygonDemo (filled, closed: Boolean);
- procedure DashDemo;
- procedure LineWidthDemo;
- procedure GraphRotDemo;
- procedure PostScriptComments;
-
-
- implementation
-
- function RotatePt (p, c: Point; ca, sa: Extended): Point;
- { Rotates point p around center c using ca = cos(angle), sa = sin(angle) }
- { and returns rotated point as function value. }
- var
- t: Integer;
- begin
- t := p.h - c.h;
- p.h := c.h + Round(ca * t - sa * (p.v - c.v));
- p.v := c.v - Round(-sa * t - ca * (p.v - c.v));
- RotatePt := p;
- end;
-
- procedure QDRotatedRect (r: Rect; ctr: Point; angle: Fixed);
- var
- ca, sa, t: Extended;
- pt, tl, tr, bl, br: Point;
- begin
- t := 3.14159 / 180; { convert degrees to radians for sin, cos }
- t := t * angle / 65536; { convert Fixed angle to Extended }
- ca := cos(t);
- sa := -sin(t); { because of QuickDraw's mathematically negative y-direction }
- PenNormal;
- GetPen(pt);
- AddPt(pt, ctr);
- tl := r.topLeft;
- br := r.botRight;
- SetPt(tr, br.h, tl.v);
- SetPt(bl, tl.h, br.v);
-
- tl := RotatePt(tl, ctr, ca, sa);
- tr := RotatePt(tr, ctr, ca, sa);
- bl := RotatePt(bl, ctr, ca, sa);
- br := RotatePt(br, ctr, ca, sa);
-
- MoveTo(tl.h, tl.v);
- LineTo(tr.h, tr.v);
- LineTo(br.h, br.v);
- LineTo(bl.h, bl.v);
- LineTo(tl.h, tl.v);
-
- MoveTo(pt.h, pt.v);
- end;
-
-
- procedure QDStringRotation (s: Str255; ctr: Point; just, flip: Integer; rot: Fixed);
- { QDStringRotation provides a QuickDraw substitute for the PostScript feature. }
- { May contain any QuickDraw imaging, except picture comments. }
- { Left as an exercise for the reader ... }
- var
- ca, sa, t: Extended;
- info: FontInfo;
- r: Rect;
- pt: Point;
- ts: Str255;
- saveFont, saveSize, x, y: Integer;
- begin
- PenNormal;
- GetPen(pt);
-
- {• DrawString(s); -- This would show the non-rotated string! •}
-
- GetFontInfo(info);
- SetRect(r, pt.h, pt.v - info.ascent, pt.h + StringWidth(s), pt.v + info.descent);
- QDRotatedRect(r, ctr, rot);
-
- AddPt(pt, ctr);
- SetRect(r, ctr.h - 3, ctr.v - 3, ctr.h + 3, ctr.v + 3);
- FrameOval(r); { the center of the rotation }
-
- GetIndString(ts, rStrings, iCommentRotate); { explain the rotated boxes }
- saveFont := thePort^.txFont;
- saveSize := thePort^.txSize;
- TextFont(applFont);
- TextSize(12);
- with thePort^.portRect do
- begin
- x := left + ((right - left - StringWidth(ts))) div 2;
- y := bottom - 20;
- end;
- MoveTo(x, y);
- DrawString(ts);
- TextFont(saveFont);
- TextSize(saveSize);
- MoveTo(pt.h, pt.v); { preserve pen location }
- end;
-
- procedure DrawXString (s: Str255; ctr: Point; just, flip: Integer; rot: Fixed);
- { Draws the string s rotated by rot degrees around the current point, offset }
- { by ctr, justifying and flipping according to the just and flip parameters. }
- { If printed to a PostScript device, the rotation is done by the PostScript }
- { interpreter; if the printer driver does not recognize the PostScriptBegin }
- { and PostScriptEnd picture comments, the external procedure QDStringRotation }
- { is used to image the rotated string. The pen position is preserved. }
-
- var
- hT: TTxtPicHdl; { defined in PicComments.p - see Appendix }
- hC: TCenterHdl; { –"– }
- zeroRect: Rect;
- pt: Point;
- oldClip: RgnHandle;
-
- begin
- GetPen(pt); { to preserve the pen position }
-
- { This is for non-PostScript printers: }
- { ------------------------------------ }
- PicComment(PostScriptBegin, 0, nil);
- QDStringRotation(s, ctr, just, flip, rot);
- PicComment(PostScriptEnd, 0, nil);
-
- { The following is for PostScript printers only: }
- { ---------------------------------------------- }
-
- {• QDStringRotation(s, ctr, just, flip, rot); •}
- { This may be useful to demonstrate the effects of just/flip }
-
-
- hT := TTxtPicHdl(NewHandle(SizeOf(TTxtPicRec)));
- hC := TCenterHdl(NewHandle(SizeOf(TCenterRec)));
- { no error handling: if these fail, we are in deep trouble anyway ...}
-
- hT^^.tJus := just;
- hT^^.tFlip := flip;
- hT^^.tAngle := -FixRound(rot); { I like counter-clockwise better }
- hT^^.tLine := 0; { reserved }
- hT^^.tCmnt := 0; { used internally by the printer driver }
- hT^^.tAngleFixed := -rot;
-
- hC^^.y := Long2Fix(ctr.v);
- hC^^.x := Long2Fix(ctr.h);
-
- PicComment(TextBegin, SizeOf(TTxtPicRec), Handle(hT));
- PicComment(TextCenter, SizeOf(TCenterRec), Handle(hC));
- { PostScript graphics state now has rotated/flipped coordinates }
-
- { Hide the following DrawString from QuickDraw }
- oldClip := NewRgn;
- GetClip(oldClip);
- SetRect(zeroRect, 0, 0, 0, 0);
- ClipRect(zeroRect);
- { The PostScript driver ignores clipping between TextBegin and TextEnd }
- DrawString(s); { in the rotated PostScript environment }
- ClipRect(oldClip^^.rgnBBox);
-
- PicComment(TextEnd, 0, nil);
- { Set PostScript's environment back to the original state }
-
- DisposHandle(Handle(hT));
- DisposHandle(Handle(hC));
-
- MoveTo(pt.h, pt.v); { to preserve the pen position }
- end;
-
- procedure TextRotationDemo;
- const
- x0 = 140;
- y0 = 140;
- fontSize = 36;
- var
- ctr: Point;
- familyID: Integer;
- angle: Fixed;
- fontName, s: Str255;
- begin
- GetIndString(fontName, rFontNames, iTextRotFont);
- GetFNum(fontName, familyID);
- TextFont(familyID);
- TextSize(fontSize);
- TextFace([]);
- GetIndString(s, rStrings, iRotatedText);
- ctr.v := 0;
- ctr.h := StringWidth(s) div 2;
- MoveTo(x0, y0);
- angle := Long2Fix(45);
- DrawXString(s, ctr, gJus, gFlip, angle);
- angle := Long2Fix(-30);
- DrawXString(s, ctr, gJus, gFlip, angle);
- end;
-
- {-------------------------------------------------------------------------------}
- procedure LineLayoutDemo;
- const
- fontSize = 14;
- x0 = 20; { starting point }
- y0 = 40;
- h = 30; { line height }
- var
- familyID: Integer;
- w, y: Integer;
- fontName, s1, s2: Str255;
-
- procedure DrawSpacedCharacters (vPos: Integer; addStrCmt: Boolean);
- const
- firstChar = 'a';
- lastChar = 'z';
- d = 12;
- var
- i: Integer;
- begin
- MoveTo(x0, vPos);
- for i := 0 to ord(lastChar) - ord(firstChar) do
- begin
- if addStrCmt then
- PicComment(StringBegin, 0, nil);
- MoveTo(x0 + i * d, vPos);
- DrawChar(chr(ord(firstChar) + i));
- if addStrCmt then
- PicComment(StringEnd, 0, nil);
- end;
- for i := 0 to ord(lastChar) - ord(firstChar) do
- begin
- MoveTo(x0 + i * d, vPos + 4);
- Line(0, 3);
- end;
- end; { DrawSpacedCharacters }
-
- begin { LineLayoutDemo }
- GetIndString(fontName, rFontNames, iLLFont1);
- GetIndString(s1, rStrings, iLineLayout1);
- GetIndString(s2, rStrings, iLineLayout2);
- GetFNum(fontName, familyID);
- TextFont(familyID);
- TextSize(fontSize);
- w := StringWidth(s1);
- y := y0;
- MoveTo(x0 + w, y - h);
- Line(0, 9 * h div 2); { this is to estimate the difference }
-
- MoveTo(x0, y);
- DrawString(s1);
- MoveTo(x0 + w, y);
- DrawString(s2);
- y := y + h;
-
- PicComment(LineLayoutOff, 0, nil);
-
- MoveTo(x0, y);
- DrawString(s1);
- MoveTo(x0 + w, y);
- DrawString(s2);
- y := y + h;
-
- PicComment(StringBegin, 0, nil); { see comment in TN #91 ! }
- MoveTo(x0, y);
- DrawString(s1);
- PicComment(StringEnd, 0, nil);
-
- MoveTo(x0 + w, y);
- DrawString(s2);
- y := y + h;
-
- PicComment(LineLayoutOn, 0, nil);
- MoveTo(x0, y);
- DrawString(s1);
- MoveTo(x0 + w, y);
- DrawString(s2);
- y := y + 3 * h div 2;
-
- GetIndString(fontName, rFontNames, iLLFont2);
- GetFNum(fontName, familyID);
- TextFont(familyID);
-
- DrawSpacedCharacters(y, FALSE);
- y := y + h;
- PicComment(LineLayoutOff, 0, nil);
- DrawSpacedCharacters(y, FALSE);
- y := y + h;
- DrawSpacedCharacters(y, TRUE); { StringBegin/StringEnd with each character }
- PicComment(LineLayoutOn, 0, nil); { restore default }
- end; { LineLayoutDemo }
-
- {-------------------------------------------------------------------------------}
- procedure PolygonDemo (filled, closed: Boolean);
- const
- kN = 4; { number of vertices for PostScript}
- kA = 6; { " for QD approximation of smoothed "not closed" polygon }
- kB = 8; { " for QD approximation of smoothed "closed" polygon }
- type
- PointArray = array[0..0] of Point; { Range checking OFF }
- PointArrayPtr = ^PointArray;
- var
- p, qa, qb: PointArrayPtr;
- aPolyVerbH: TPolyVerbHdl;
- i: Integer;
- clipRgn, polyRgn: RgnHandle;
- zeroRect: Rect;
-
- procedure DefineVertices (p, qa, qb: PointArrayPtr);
- const
- cx = 240;
- cy = 150;
- r0 = 130;
- var
- d, i: Integer;
- begin
- { The array p^ contains the array of the control points for the Bézier curve: }
- SetPt(p^[0], cx + r0, cy);
- SetPt(p^[1], cx, cy + r0);
- SetPt(p^[2], cx - r0, cy);
- SetPt(p^[3], cx, cy - r0);
- p^[4] := p^[0];
- d := round(0.7 * (p^[1].v - cy));
-
- qa^[0] := p^[0];
- { qa^ contains the points for a crude polygon approximation of the }
- { smoothed curve, with "closed" = FALSE }
- SetPt(qa^[1], cx, cy + d);
- SetPt(qa^[2], (p^[1].h + p^[2].h) div 2, (p^[1].v + p^[2].v) div 2);
- SetPt(qa^[3], cx + round(0.8 * (p^[2].h - cx)), cy);
- SetPt(qa^[4], qa^[2].h, cy + cy - qa^[2].v);
- SetPt(qa^[5], qa^[1].h, cy + cy - qa^[1].v);
- qa^[6] := qa^[0];
- { qb^ contains the points for a crude polygon approximation of the }
- { smoothed curve, with "closed" = TRUE }
- SetPt(qb^[0], cx + d, cy);
- SetPt(qb^[2], cx, cy + d);
- SetPt(qb^[4], cx - d, cy);
- SetPt(qb^[6], cx, cy - d);
- for i := 0 to 3 do
- SetPt(qb^[2 * i + 1], (p^[i].h + p^[i + 1].h) div 2, (p^[i].v + p^[i + 1].v) div 2);
- qb^[8] := qb^[0];
- end; { DefineVertices}
-
- begin { PolygonDemo }
- p := PointArrayPtr(NewPtr(SizeOf(Point) * (kN + 1)));
- qa := PointArrayPtr(NewPtr(SizeOf(Point) * (kA + 1)));
- qb := PointArrayPtr(NewPtr(SizeOf(Point) * (kB + 1)));
- if (p = nil) or (qa = nil) or (qb = nil) then
- DebugStr('NewPtr failed');
- DefineVertices(p, qa, qb);
-
- PenNormal; { First show the standard QuickDraw polygon }
- MoveTo(p^[0].h, p^[0].v);
- for i := 1 to kN do
- LineTo(p^[i].h, p^[i].v);
-
- PenSize(2, 2); { Now the same polygon "smoothed" }
- PenPat(gray);
- { First, the PostScript representation, clipped off from QuickDraw: }
- aPolyVerbH := TPolyVerbHdl(NewHandle(SizeOf(TPolyVerbRec)));
- if aPolyVerbH <> nil then
- with aPolyVerbH^^ do
- begin
- fPolyClose := closed;
- fPolyFrame := TRUE;
- fPolyFill := filled;
- f3 := FALSE;
- f4 := FALSE;
- f5 := FALSE;
- f6 := FALSE;
- f7 := FALSE;
- end;
- MoveTo(p^[0].h, p^[0].v);
- PicComment(PolyBegin, 0, nil);
- if closed then
- PicComment(PolyClose, 0, nil);
- PicComment(PolySmooth, SizeOf(TPolyVerbRec), Handle(aPolyVerbH));
- clipRgn := NewRgn;
- GetClip(clipRgn);
- ClipRect(zeroRect);
- for i := 1 to kN do
- LineTo(p^[i].h, p^[i].v);
-
- { Next, the -crude- QuickDraw approximation of the smoothed polygon, }
- { invisible for PostScript because of PolyIgnore: }
- SetClip(clipRgn);
- PicComment(PolyIgnore, 0, nil);
- polyRgn := NewRgn;
- OpenRgn;
- if not closed then
- begin
- MoveTo(qa^[0].h, qa^[0].v);
- for i := 1 to kA do
- LineTo(qa^[i].h, qa^[i].v);
- end
- else
- begin
- MoveTo(qb^[0].h, qb^[0].v);
- for i := 1 to kB do
- LineTo(qb^[i].h, qb^[i].v);
- end;
- CloseRgn(polyRgn);
- FrameRgn(polyRgn);
-
- if filled then
- FillRgn(polyRgn, gray);
-
- PicComment(PolyEnd, 0, nil);
-
- DisposHandle(Handle(aPolyVerbH));
- DisposeRgn(polyRgn);
- DisposPtr(Ptr(p));
- DisposPtr(Ptr(qa));
- DisposPtr(Ptr(qb));
- end; { PolygonDemo }
-
-
- procedure DashDemo;
- const
- cx = 250;
- cy = 240;
- r0 = 200;
-
- var
- dashHdl: TDashedLineHdl;
- i: Integer;
- a, rad: Extended;
-
- procedure DashedQDLine (dx, dy: Integer; dashSpec: TDashedLineHdl);
- var
- oldPat: Pattern;
- begin
- oldPat := thePort^.pnPat;
- PenPat(gray);
- Line(dx, dy);
- PenPat(oldPat);
- end;
-
- begin
- PenSize(3, 3);
- { First the PostScript picture comment version. }
- { The "magic pen mode" 23 makes the line drawing invisible for QuickDraw. }
- PenMode(magicPen);
- dashHdl := TDashedLineHdl(NewHandle(SizeOf(TDashedLineRec)));
- if dashHdl <> nil then
- with dashHdl^^ do
- begin
- offset := 2; { just for fun}
- centered := 0; { currently ignored - set to 0 }
- intervals[0] := 2;
- intervals[1] := 4;
- intervals[2] := 8; { this means 4 points on, 8 points off }
- end;
- PicComment(DashedLine, SizeOf(TDashedLineRec), Handle(dashHdl));
- rad := 3.14159 / 180; { conversion degrees -> radians }
- for i := 0 to 9 do
- begin { draw some dashed lines }
- a := i * 20 * rad;
- MoveTo(cx, cy);
- Line(round(r0 * cos(a)), -round(r0 * sin(a)));
- end;
- PicComment(DashedStop, 0, nil); { That's enough! }
- DisposHandle(Handle(dashHdl));
- PenMode(srcOr); { No magic any more. }
-
- { Now, the QuickDraw version. The PostScript driver must ignore it, }
- { so we enclose it between PostScriptBegin and PostScriptEnd comments.}
- PicComment(PostScriptBegin, 0, nil);
- for i := 0 to 9 do
- begin
- MoveTo(cx, cy);
- DashedQDLine(round(r0 * cos(i * 20 * rad)), -round(r0 * sin(i * 20 * rad)), dashHdl);
- end;
- PicComment(PostScriptEnd, 0, nil);
- end;
-
-
- procedure SetNewLineWidth (oldWidth, newWidth: TLineWidth);
- var
- tempWidthH: TLineWidthHdl;
- begin
- tempWidthH := TLineWidthHdl(NewHandle(SizeOf(TLineWidth)));
- { If tempWidthH = NIL we are screwed anyway }
- tempWidthH^^.v := oldWidth.h;
- tempWidthH^^.h := oldWidth.v;
- PicComment(SetLineWidth, SizeOf(TLineWidth), Handle(tempWidthH));
- tempWidthH^^ := newWidth;
- PicComment(SetLineWidth, SizeOf(TLineWidth), Handle(tempWidthH));
- DisposHandle(Handle(tempWidthH));
- end;
-
-
- procedure LineWidthDemo;
- const
- y0 = 50; { topleft of demo }
- x0 = 50;
- d0 = 400; { length of horizontal lines }
- e0 = 5; { distance between lines }
- kN = 15; { number of lines }
- var
- oldWidth, newWidth: TLineWidth; { actuall a "Point" }
- i, j, y: Integer;
- begin
- PenNormal;
- y := y0;
- SetPt(oldWidth, 1, 1); { initial linewidth = 1.0 }
- for i := 1 to kN do
- begin
- SetPt(newWidth, 4, i);
- { want to set it to i/4 = 0.25, 0.50, 0.75 ... }
- SetNewLineWidth(oldWidth, newWidth);
- MoveTo(x0, y);
- Line(d0, 0);
- y := y + e0;
- oldWidth := newWidth;
- end;
- end;
-
-
- procedure PSRotatedRect (r: Rect; offset: Point; angle: Fixed);
- { Does the rectangle rotation for the PostScript LaserWriter driver. }
- { Uses the RotateCenter, RotateBegin and RotateEnd picture comments, }
- { and the "magic" pen mode 23 to hide the drawing from QuickDraw. }
- var
- rInfo: TRotationHdl;
- rCenter: TCenterHdl;
- oldPenMode: Integer;
- begin
- rInfo := TRotationHdl(NewHandle(SizeOf(TRotationRec)));
- rCenter := TCenterHdl(NewHandle(SizeOf(TCenterRec)));
- if (rInfo = nil) or (rCenter = nil) then
- DebugStr('NewHandle failed');
- with rInfo^^ do
- begin
- rFlip := 0;
- rAngle := -FixRound(angle);
- rAngleFixed := -angle;
- end;
- with rCenter^^ do
- begin
- x := Long2Fix(offset.h);
- y := Long2Fix(offset.v);
- end;
- MoveTo(r.left, r.top);
- FlushGrafPortState;
- PicComment(RotateCenter, SizeOf(TCenterRec), Handle(rCenter));
- PicComment(RotateBegin, SizeOf(TRotationRec), Handle(rInfo));
- oldPenMode := thePort^.pnMode;
- PenMode(magicPen);
- FrameRect(r);
- PenMode(oldPenMode);
- PicComment(RotateEnd, 0, nil);
- DisposeHandle(Handle(rInfo));
- DisposeHandle(Handle(rCenter));
- end;
-
- procedure GraphRotDemo;
- const
- angle = 30;
- var
- spinRect: Rect;
- delta: Point;
- begin
- SetRect(spinRect, 100, 100, 300, 200);
- with spinRect do
- SetPt(delta, (right - left) div 2, (bottom - top) div 2);
-
- PenSize(2, 2);
- PenPat(ltGray);
- FrameRect(spinRect); { show the unrotated square }
- PenNormal;
-
- PSRotatedRect(spinRect, delta, Long2Fix(angle));
-
- { QuickDraw equivalent of the rotated object, hidden from PostScript driver }
- { because of PostScriptBegin and PostScriptEnd }
- PicComment(PostScriptBegin, 0, nil);
- QDRotatedRect(spinRect, delta, Long2Fix(angle));
- PicComment(PostScriptEnd, 0, nil);
- end;
-
-
- procedure PostScriptLine (s: Str255);
- { A utility procedure to transmit a string of PostScript code through }
- { the PostScriptHandle picture comment to the PostScript printer. }
- { It should be called only between PostScriptBegin and PostScriptEnd }
- { picture comments. }
-
- var
- h: Handle;
-
- begin
- h := NewHandle(256);
- if h = nil then
- DebugStr('NewHandle failed');
- BlockMove(@s[1], h^, Length(s));
- PicComment(PostScriptHandle, Length(s), h);
- h^^ := 13;
- PicComment(PostScriptHandle, 1, h); { add a carriage return }
- DisposeHandle(h);
- end;
-
-
- procedure PostScriptComments;
- begin
- { First, the simple example: }
- PicComment(PostScriptBegin, 0, nil);
- PostScriptLine('100 100 moveto 0 100 rlineto 100 0 rlineto ');
- PostScriptLine('0 -100 rlineto -100 0 rlineto');
- PostScriptLine('stroke');
-
- TextFont(applFont);
- TextSize(12);
- MoveTo(30, 30);
- DrawString('This text does not appear on PostScript devices');
- PicComment(PostScriptEnd, 0, nil);
-
- { Now, a new PostScript definition you want to keep in the }
- { userdict. If you used PostScriptBegin, the definition would }
- { be lost when PostScriptEnd is encountered, because the state }
- { previous to the PostScriptBegin comment would be restored. }
- PicComment(PSBeginNoSave, 0, nil);
- PostScriptLine('userdict begin');
- PostScriptLine('/myFrameRect {');
- PostScriptLine('250 250 moveto 0 100 rlineto');
- PostScriptLine('200 0 rlineto 0 -100 rlineto -200 0 rlineto ');
- PostScriptLine('stroke } def');
- PostScriptLine('end');
- PicComment(PostScriptEnd, 0, nil);
-
- { Let's test if the definition from above is still available. }
- { This assumes that no font downloading has occurred. }
-
- PicComment(PostScriptBegin, 0, nil);
- PostScriptLine('//userdict /myFrameRect get exec ');
- PicComment(PostScriptEnd, 0, nil);
- end;
-
- end.